﻿using System.ComponentModel;
using Dgg.LearnYourTools.NUnit.C_Assertions.Support;
using NUnit.Framework;
using NUnit.Framework.Constraints;

namespace Dgg.LearnYourTools.NUnit.C_Assertions.f_CustomConstraints
{
	[TestFixture]
	public class xxy_GettingSmarterWithEvents
	{
		[Test]
		public void AssertOnEvents_FancySyntax()
		{
			var subject = new EventRaisingSubject();

			Assert.That(() => { subject.I = 3; }, Raises
				.PropertyChanging
				.For(subject)
				.WithPropertyName("I"));

			/*aPropertyRaisesEventWithName(subject, s => s.I = 3, "I");
			aPropertyRaisesEventWithName(subject, s => s.I = 4, "I");
			aPropertyRaisesEventWithName(subject, s => s.S = "3", "S");
			aPropertyRaisesEventWithName(subject, s => s.S = "4", "S");*/
		}
	}

	public static class Raises
	{
		public static PropertyChanging PropertyChanging { get { return new PropertyChanging(); } }
	}

	public class PropertyChanging
	{
		public WithPropertyChanging<TSubject> For<TSubject>(TSubject subject) where TSubject : INotifyPropertyChanging
		{
			return new WithPropertyChanging<TSubject>(subject);
		}
	}

	public class WithPropertyChanging<TSubject> where TSubject : INotifyPropertyChanging
	{
		private readonly TSubject _subject;

		public WithPropertyChanging(TSubject subject)
		{
			_subject = subject;
		}

		public IResolveConstraint WithPropertyName(string expectedPropertyName)
		{
			return new PropertyChangingConstraint<TSubject>(_subject, expectedPropertyName);
		}
	}

	public class PropertyChangingConstraint<TSubject> : Constraint where TSubject : INotifyPropertyChanging
	{
		private readonly TSubject _subject;
		private readonly string _expectedPropertyName;

		public PropertyChangingConstraint(TSubject subject, string expectedPropertyName)
		{
			_subject = subject;
			_expectedPropertyName = expectedPropertyName;
		}

		Constraint _failed;
		public override bool Matches(object actual)
		{
			bool matched = false;
			TestDelegate current = (TestDelegate)actual;

			_subject.PropertyChanging += (sender, e) =>
				{
					var sameSender = new SameSenderConstraint(sender);
					matched = sameSender.Matches(_subject);
					if (matched)
					{
						var equalToProperty = new EqualToProperty(_expectedPropertyName);
						matched = equalToProperty.Matches(e.PropertyName);
						if (!matched) _failed = equalToProperty;
					}
					else
					{
						_failed = sameSender;
					}
				};
			current();
			return matched;
		}

		public override void WriteDescriptionTo(MessageWriter writer)
		{
			_failed.WriteDescriptionTo(writer);
		}

		public override void WriteActualValueTo(MessageWriter writer)
		{
			writer.WriteActualValue(_subject.GetHashCode());
		}

		public override void WriteMessageTo(MessageWriter writer)
		{
			_failed.WriteMessageTo(writer);
		}

		class SameSenderConstraint : SameAsConstraint
		{
			public SameSenderConstraint(object expected) : base(expected) { }

			public override void WriteMessageTo(MessageWriter writer)
			{
				writer.WriteMessageLine(0, "sender");
				base.WriteMessageTo(writer);
			}
		}

		class EqualToProperty : EqualConstraint
		{
			public EqualToProperty(object expected) : base(expected) { }

			public override void WriteMessageTo(MessageWriter writer)
			{
				writer.WriteMessageLine(0, "e.PropertyName");
				base.WriteMessageTo(writer);
			}
		}
	}




}
